package edu.pku.sei.transformation.main;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import edu.pku.sei.bridge.mdabase.adapter.EMFXMILoader;
import edu.pku.sei.mdabase.infrastructure.model.MModelGraph;
import edu.pku.sei.mdabase.infrastructure.runtime.Context;
import edu.pku.sei.transformation.mtparser.Ast;
import edu.pku.sei.transformation.semantics.TransformationController;
import edu.pku.sei.transformation.serialization.EMFXMISaver;
import edu.pku.sei.transformation.serialization.TransformationLoader;
import edu.pku.sei.transformation.structure.Rule;
import edu.pku.sei.transformation.structure.RuleBasedEnvironment;
import edu.pku.sei.transformation.structure.Trace;
import edu.pku.sei.transformation.structure.Transformation;
import edu.pku.sei.transformation.structure.TypedModel;
import edu.pku.sei.transformation.visitor.MTControllerVisitor;
import edu.pku.sei.transformation.visitor.MTScriptParser;

public class MoTiFRunner
{
	private String src = null;
	private String tar = null;
	private String srcMetamodel;
	private String tarMetamodel;
	private String transFile = null;
	private String srcModelFile = null;
	private String tarModelFile = null;
	private String semanticFile = null;
	
	private RuleBasedEnvironment env;
	private Context base;
	private Trace trace = new Trace();
	private Transformation trans = null;
	
	public Transformation getTrans()
	{
		return trans;
	}

	public void setTrans(Transformation trans)
	{
		this.trans = trans;
	}

	public RuleBasedEnvironment getEnv()
	{
		if (env == null)
			env = new RuleBasedEnvironment();
		return env;
	}
	
	public Context getBaseContext()
	{
		return base;
	}
	
	public Trace getTrace()
	{
		return trace;
	}
	
	public MoTiFRunner(String trans, String src, String tar, String srcModel, String tarModel, String semantic)
	{
		transFile = trans;
		this.src = src;
		this.tar = tar;
		srcModelFile = srcModel;
		tarModelFile = tarModel;
		this.semanticFile = semantic;
	}
	
	public void run() throws IOException, ParserConfigurationException, SAXException
	{
		PrintStream o = System.out;
		if (transFile == null)
		{
			o.println("Transformation file not found!");
			return;
		}
		if (semanticFile == null)
		{
			o.println("Semantics file not found!");
			return;
		}
		File file = new File(semanticFile);
		InputStream input = new FileInputStream(file);
		long len = file.length();
		byte[] temp = new byte[(int) len];
		input.read(temp);
		String content = new String(temp);
		input.close();
		Ast ast = MTScriptParser.parse(content);
		if (ast == null)
		{
			o.println("Semantics script parsing failed!");
			return;
		}
		System.out.println("Semantics script parsing done");
		
		init();
		loadModels();
		execute("enforce", ast);
	}
	
	protected void init() throws ParserConfigurationException, SAXException, IOException
	{
		env = new RuleBasedEnvironment();
		base = new Context();
		TransformationLoader loader = new TransformationLoader();
		File file = new File(transFile);
		trans = loader.load(file);
		env.setTransformation(trans);
		for (Rule rule : trans.getOwnedRules())
		{
			env.registRule(rule.getName(), rule);
		}
		srcMetamodel = trans.getFormalParameter(src).getType().getName();
		tarMetamodel = trans.getFormalParameter(tar).getType().getName();
		base.setEnvironment(env);
	}
	
	protected void loadModels()
	{
		TypedModel sourceTypedModel = env.getTransformation().getFormalParameter(src);
		TypedModel targetTypedModel = env.getTransformation().getFormalParameter(tar);
		MModelGraph sourceModel = null, targetModel= null;
		EMFXMILoader srcloader = new EMFXMILoader(srcMetamodel);
		sourceModel = srcloader.load(srcModelFile);
		if (sourceModel == null)
			System.out.println("loading source model failed");
		sourceModel.setTypeGraph(sourceTypedModel.getType().getMetamodel());
		
		EMFXMILoader tarloader = new EMFXMILoader(tarMetamodel);
		targetModel = tarloader.load(tarModelFile);
		if (targetModel == null)
		{
			System.out.println("loading target model failed, creating a new one");
			targetModel = new MModelGraph();
		}
		targetModel.setTypeGraph(targetTypedModel.getType().getMetamodel());
		
		env.addTargetmodel(targetTypedModel);
		env.registerModel(sourceTypedModel, sourceModel);
		env.registerModel(targetTypedModel, targetModel);
	}
	
	protected void execute(String mode, Ast ast)
	{
		MTControllerVisitor visitor = new MTControllerVisitor(getEnv(), getBaseContext(), getTrace());
		ast.accept(visitor);
		env.setMTVisitor(visitor);
		System.out.println("Semantics analyzing done");
		
		for (Rule rule : getTrans().getOwnedRules())
		{
			if (!rule.isTop())
				continue;
			String ruleName = rule.getName();
			TransformationController.instance.registVariable(getBaseContext(), rule);
			Object result = env.getMTVisitor().invokeSemantic(ruleName, null, mode);
			System.out.println("Rule " + ruleName + " has finished execution. Result = " + result);
		}
		if (mode.equals("enforce"))
		{
			EMFXMISaver saver = new EMFXMISaver();
			MModelGraph model = env.getModelByTypedModel(env.getTransformation().getFormalParameter(tar));
			saver.save(model, tarModelFile);
		}
	}
}
